package es.usc.citius.servando.calendula.util.stock;

import org.joda.time.Duration;
import org.joda.time.LocalDate;


public class StockCalculator {
    public static final int MAX_STOCK_DURATION_MONTHS = 3;

    /**
     * Calculates how long the stock in [currentStock] will last, starting from [startDate]
     * and with the stock consumption given by [stockProvider], up to a maximum of [MAX_STOCK_DURATION_MONTHS]
     *
     * @param startDate     the start date for the calculation
     * @param stockProvider a [StockForDayProvider] that specifies how much stock would be consumed for a given date
     * @param currentStock  the base quantity of stock
     * @return a [StockEnd.OnDate] when the result is calculable, a [StockEnd.OverMax] if it's ouside the simulation limits
     */
    public static StockEnd calculateStockEnd(LocalDate startDate, StockForDayProvider stockProvider, float currentStock) {
        LocalDate endDate = startDate.plusMonths(MAX_STOCK_DURATION_MONTHS);

        LocalDate virtualDate = new LocalDate(startDate);
        float virtualStock = currentStock;

        // simulate daily intakes until the stock runs out (or we reach the max duration)
        do {
            float stockForDay = stockProvider.stockNeededForDay(virtualDate);
            virtualStock -= stockForDay;

            if (virtualStock < 0) {
                return new StockEnd.OnDate(
                        virtualDate,
                        new Duration(
                                startDate.toDateTimeAtStartOfDay(),
                                virtualDate.toDateTimeAtStartOfDay()
                        ).getStandardDays()
                );
            }

            virtualDate = virtualDate.plusDays(1);
        } while (virtualDate.isBefore(endDate));

        return StockEnd.OverMax.instance;
    }

    public static class StockEnd {
        /**
         * The estimation overstepped the maximum simulation time
         */
        public static class OverMax extends StockEnd {
            private static OverMax instance;

            private OverMax() {
            }

            public static OverMax getInstance() {
                if (instance == null) {
                    synchronized (OverMax.class) {
                        if (instance == null) {
                            instance = new OverMax();
                        }
                    }
                }
                return instance;
            }
        }

        public static class OnDate extends StockEnd {
            private LocalDate date;
            private Long days;

            /**
             * The stock ends on `date`
             *
             * @param date the stock end date
             * @param days the duration of the stock in days
             */
            public OnDate(LocalDate date, Long days) {
                this.date = date;
                this.days = days;
            }

            public LocalDate getDate() {
                return date;
            }

            public Long getDays() {
                return days;
            }

        }
    }
}
